#include "ExpressionManager.h"
#include "FeatureShape/HistoryManager.h"
#include <string>
#include <regex>
#include <set>
#include <iostream>
#include <BRepPrimAPI_MakeCone.hxx>
#include <BRepPrimAPI_MakeBox.hxx>
#include <BRepPrimAPI_MakeCylinder.hxx>
#include <BRepPrimAPI_MakeSphere.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>


ExpressionManager::ExpressionManager(HistoryManager* historyManager)
	: m_historyManager(historyManager)
{

}

void ExpressionManager::AddExpression(QShapeVersion* shapeVersion)
{
	bool bContains = versionToExpressions.contains(shapeVersion->m_versionID);
	QList<std::shared_ptr<Expression>> expressions = GenerateExpressions(shapeVersion);
	if (bContains)
	{
		auto item = versionToExpressions.find(shapeVersion->m_versionID);
		item->clear();
		*item = expressions;
	}
	else
	{
		versionToExpressions.insert(shapeVersion->m_versionID, expressions);
	}
}

std::shared_ptr<Expression> ExpressionManager::AddCustomExpression()
{
	std::shared_ptr<Expression> newExpression = std::make_shared<Expression>(false);
	return newExpression;
}

void ExpressionManager::CustomExpressionConfirmKey(const std::shared_ptr<Expression>& expression)
{
    if (expression->IsNativeExpression())
    {
        return;
    }

	// 先检查是否存在该表达式，若存在将其删除
	DeleteExpression(expression);

	// 查询到该表达式应该对应到哪一个version
	// #TODO:这里可以优化，考虑存储一个versionID-FeatureKey的映射，减少查找次数
	uint versionID = -1;
    for (auto it = versionToExpressions.begin(); it != versionToExpressions.end(); ++it)
    {
        auto iter = std::find_if(it->cbegin(), it->cend(),
            [expression](const std::shared_ptr<Expression>& e)
            {
                return e->GetTargetFeatureKey() == expression->GetTargetFeatureKey();
            });

        if (iter != it->cend())
        {
            versionID = it.key();
            break;
        }
    }

	// 添加到versionToExpressions
    if (versionID == -1)
    {
		std::cout << "ExpressionManager::CustomExpressionConfirmKey failed. can not find version for expression. Old expression has been GC?" << std::endl;
		return;
    }
	versionToExpressions[versionID].push_back(expression);
}

void ExpressionManager::ApplyExpression(std::shared_ptr<Expression> expression)
{
	// 1. 找到expression对应的version
	// 2. 调用HistoryManager的redo version

	uint versionID = -1;
	for (auto it = versionToExpressions.begin(); it != versionToExpressions.end(); ++it)
	{
		auto iter = std::find_if(it->cbegin(), it->cend(), 
			[expression](const std::shared_ptr<Expression>& e)
			{
				return *e == *expression;
			});
		
		if (iter != it->cend())
		{
			versionID = it.key();
			break;
		}
	}

	if (versionID == -1)
	{
		std::cout << "ExpressionManager::ApplyExpression failed. do not have expression" << std::endl;
		return;
	}
	m_historyManager->ModifyVersion(versionID);
}

void ExpressionManager::DeleteExpression(const std::shared_ptr<Expression>& expression)
{
    if (expression->IsNativeExpression())
    {
        return;
    }

    for (auto it = versionToExpressions.begin(); it != versionToExpressions.end(); ++it)
    {
        auto iter = std::find_if(it->cbegin(), it->cend(),
            [expression](const std::shared_ptr<Expression>& e)
            {
                return *e == *expression;
            });

        if (iter != it->cend())
        {
            it->removeOne(*iter);
            break;
        }
    }
}

QList<std::shared_ptr<Expression>> ExpressionManager::GetValidExpressions()
{
	QList<std::shared_ptr<Expression>> validExpressions;

	QList<QPointer<QShapeVersion>> historyVersion = m_historyManager->GetHistoryVersion();
	for (auto version : historyVersion)
	{
		if (versionToExpressions.contains(version->m_versionID))
		{
			validExpressions.append(*versionToExpressions.find(version->m_versionID));
		}
	}

	return validExpressions;
}

bool ExpressionManager::IsExpressionValid(const QString& expression, QList<std::shared_ptr<Feature>>& associatedFeatures)
{
	// 查找字符串中的变量
	std::string std_expression = expression.toStdString();
	std::regex re("\\b(?!sin\\b)(?!cos\\b)(?!tan\\b)[a-zA-Z_][a-zA-Z0-9_]*\\b");
	std::sregex_iterator it(std_expression.begin(), std_expression.end(), re);
	std::sregex_iterator end;
	std::set<std::string> variableSet;
	for (; it != end; ++it) 
	{
		variableSet.insert(it->str());
	}

	// 检测变量是否在特征列表中
    for (auto variable : variableSet)
	{
		QString v = QString::fromStdString(variable);
		if (!features.contains(v))
		{
			associatedFeatures.clear();
			return false;
		}
		// 添加到表达式相关联的特征列表
		associatedFeatures.push_back(features[v].lock());
	}

	return true;
}

std::shared_ptr<Feature> ExpressionManager::GetFeature(const QString& key)
{
	if (!features.contains(key))
	{
		return nullptr;
	}

	return features[key].lock();
}

QList<std::shared_ptr<Expression>> ExpressionManager::GenerateExpressions(QShapeVersion* shapeVersion)
{
	QList<std::shared_ptr<Expression>> expressions;
	ShapeOperation* op = shapeVersion->m_operation;

	if (!op)
	{
		std::cout << "Generate Expressions failed. do not have operation" << std::endl;
		return expressions;
	}

	if (op->m_operationType == EOperationType::Create)
	{
		CreateShapeOperation* createOP = static_cast<CreateShapeOperation*>(op);
		if (createOP->m_featureShape->_featureType == EFeatureType::ArcCircle)
		{
			FeatureShapeArcCircle* circle = static_cast<FeatureShapeArcCircle*>(createOP->m_featureShape.get());
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Cone)
		{
			FeatureShapeCone* cone = static_cast<FeatureShapeCone*>(createOP->m_featureShape.get());
			auto modifyOperation = [cone]()
			{
				BRepPrimAPI_MakeCone coneShape(cone->_radius1, cone->_radius2, cone->_height);
                cone->_shape = coneShape.Shape();
			};

			// radius top
			std::shared_ptr<Feature> coneTopFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cone-top"), &(cone->_radius2)));
			features[coneTopFeature->m_key] = coneTopFeature;
			coneTopFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(cone->_radius1), QString::number(cone->_radius1), coneTopFeature));

			// radius down
			std::shared_ptr<Feature> coneDownFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cone-down"), &(cone->_radius1)));
            features[coneDownFeature->m_key] = coneDownFeature;
            coneDownFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(cone->_radius1), QString::number(cone->_radius1), coneDownFeature));

			// height
			std::shared_ptr<Feature> coneHeightFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cone-height"), &(cone->_height)));
            features[coneHeightFeature->m_key] = coneHeightFeature;
            coneHeightFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(cone->_height), QString::number(cone->_height), coneHeightFeature));
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Cylinder)
		{
			FeatureShapeCylinder* cylinder = static_cast<FeatureShapeCylinder*>(createOP->m_featureShape.get());
			auto modifyOperation = [cylinder]()
			{
				BRepPrimAPI_MakeCylinder cylinderShape(cylinder->_radius, cylinder->_height);
                cylinder->_shape = cylinderShape.Shape();
			};

			// radius
			std::shared_ptr<Feature> cylinderRadiusFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cylinder-radius"), &(cylinder->_radius)));
            features[cylinderRadiusFeature->m_key] = cylinderRadiusFeature;
            cylinderRadiusFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(cylinder->_radius), QString::number(cylinder->_radius), cylinderRadiusFeature));

			// height
			std::shared_ptr<Feature> cylinderHeightFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cylinder-height"), &(cylinder->_height)));
            features[cylinderHeightFeature->m_key] = cylinderHeightFeature;
            cylinderHeightFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(cylinder->_height), QString::number(cylinder->_height), cylinderHeightFeature));
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Ellipse)
		{
			FeatureShapeEllipse* ellipse = static_cast<FeatureShapeEllipse*>(createOP->m_featureShape.get());
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Hyperbola)
		{
            FeatureShapeHyperbola* hyperbola = static_cast<FeatureShapeHyperbola*>(createOP->m_featureShape.get());
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Line)
		{
            FeatureShapeLine* line = static_cast<FeatureShapeLine*>(createOP->m_featureShape.get());
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Parabola)
		{
            FeatureShapeParabola* parabola = static_cast<FeatureShapeParabola*>(createOP->m_featureShape.get());
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Sphere)
		{
            FeatureShapeSphere* sphere = static_cast<FeatureShapeSphere*>(createOP->m_featureShape.get());
			auto modifyOperation = [sphere]()
			{
				BRepPrimAPI_MakeSphere sphereShape(sphere->_radius);
                sphere->_shape = sphereShape.Shape();
			};

			// radius
			std::shared_ptr<Feature> sphereRadiusFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("sphere-radius"), &(sphere->_radius)));
            features[sphereRadiusFeature->m_key] = sphereRadiusFeature;
            sphereRadiusFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(sphere->_radius), QString::number(sphere->_radius), sphereRadiusFeature));
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Torus)
		{
            FeatureShapeTorus* torus = static_cast<FeatureShapeTorus*>(createOP->m_featureShape.get());
			auto modifyOperation = [torus]()
			{
				BRepPrimAPI_MakeTorus torusShape(torus->_radiusInner, torus->_radiusOuter);
                torus->_shape = torusShape.Shape();
			};

			// radius inner
			std::shared_ptr<Feature> torusInnerFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("torus-Inner"), &(torus->_radiusInner)));
            features[torusInnerFeature->m_key] = torusInnerFeature;
            torusInnerFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(torus->_radiusInner), QString::number(torus->_radiusInner), torusInnerFeature));

			// radius outer
			std::shared_ptr<Feature> torusOuterFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("torus-Outer"), &(torus->_radiusOuter)));
            features[torusOuterFeature->m_key] = torusOuterFeature;
            torusOuterFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(torus->_radiusOuter), QString::number(torus->_radiusOuter), torusOuterFeature));
		}
		else if (createOP->m_featureShape->_featureType == EFeatureType::Cube)
		{
            FeatureShapeCube* cube = static_cast<FeatureShapeCube*>(createOP->m_featureShape.get());

			auto modifyOperation = [cube]()
				{
                    BRepPrimAPI_MakeBox cubeShape(cube->_width, cube->_length, cube->_height);
                    cube->_shape = cubeShape.Shape();
				};

			// width
			std::shared_ptr<Feature> cubeWidthFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cube-width"), &(cube->_width)));
            features[cubeWidthFeature->m_key] = cubeWidthFeature;
			cubeWidthFeature->setModifyOperation(modifyOperation);
			expressions.push_back(std::make_shared<Expression>(QString::number(cube->_width), QString::number(cube->_width), cubeWidthFeature));

			// length
            std::shared_ptr<Feature> cubeLenFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cube-length"), &(cube->_length)));
            features[cubeLenFeature->m_key] = cubeLenFeature;
            cubeLenFeature->setModifyOperation(modifyOperation);
            expressions.push_back(std::make_shared<Expression>(QString::number(cube->_length), QString::number(cube->_length), cubeLenFeature));

			// height
            std::shared_ptr<Feature> cubeHeightFeature(std::make_shared<Feature>(
				QString("p" + QString::number(Feature::featureCount++)),
				QString("cube-height"), &(cube->_height)));
            features[cubeHeightFeature->m_key] = cubeHeightFeature;
            cubeHeightFeature->setModifyOperation(modifyOperation);
            expressions.push_back(std::make_shared<Expression>(QString::number(cube->_height), QString::number(cube->_height), cubeHeightFeature));
		}
	}
	else if (op->m_operationType == EOperationType::Boolean)
	{
		BooleanOperation* booleanOP = static_cast<BooleanOperation*>(op);
	}
	else if (op->m_operationType == EOperationType::Delete)
	{
        DeleteShapeOperation* deleteOP = static_cast<DeleteShapeOperation*>(op);
	}
	else if (op->m_operationType == EOperationType::Hollow)
	{
        HollowOperation* hollowOP = static_cast<HollowOperation*>(op);
	}
	else if (op->m_operationType == EOperationType::Transform)
	{
        TransformOperation* transformOP = static_cast<TransformOperation*>(op);
	}
	else if (op->m_operationType == EOperationType::Stretch)
	{
        StretchOperation* stretchOP = static_cast<StretchOperation*>(op);
	}
	else if (op->m_operationType == EOperationType::Revolve)
	{
        RevolveOperation* revolveOP = static_cast<RevolveOperation*>(op);
	}
	else if (op->m_operationType == EOperationType::RoundOff)
	{
        RoundOffOperation* roundOffOP = static_cast<RoundOffOperation*>(op);
	}
	

	return expressions;
}
